先考慮到http response各個部份的需求,把這部份的實做確定下來。
與伺服器流程與機制整合
對http來說,伺服器的回應,主要就是由header與body組合而成,而且header必須在body之前送出,body則可以一個chuck一個chuck送出,直到結束。因為header與body必須有這樣先後的關係,在設計上,最好能保證這樣的輸出順序。另外,response還有一個特別的功能是addTrailers(),可以利用他來送出trailer header。trailer header有一些特別的條件:必須在header中有trailer這個header,然後在body之後送出trailer headers,最後才結束response。
有一個簡單的方法可以達到目的,並且把它與伺服器的流程做結合:
* 在route handler中,可以隨時呼叫response.setHeader/getHeader/deleteHeader,但是無法直接使用response.write寫入到response,而是把要輸出的東西放進output buffer
* 同樣在route handler中,可以隨時呼叫response.addTrailers,但是它也會把trailer header寫入到output buffer中
* 在post dispatch才進行reponse.write,把output buffer的資料寫入到response,所有資料輸出後,呼叫response.addTrailers來輸出trailer headers,最後才呼叫response.end來結束request事件
不過要做到的這樣的功能,需要重新包裝傳給route handler的response物件。
這樣做還有一個好處,除了保證輸出的順序是正確的,因為操作介面對使用者來說並沒有變,所以對使用者的習慣來說,並沒有影響。如果要在route handler(也就是controller)中引入template引擎,也只要把他的輸出直接轉到response.write就可以,對於要使用怎樣的視圖機制,影響都不大。
另外,原先在伺服器程式中,設計好了一個用來render的callback函數。現在還是使用這個callback函數來負責render,只要傳給它content type以及http response body的資料就可以。是否需要調整,等到實做template引擎,與這部份搭配看看就知道是否還需要調整。
實做
就先來實做看看,這樣是否可行吧。
原始碼請參考github:
https://github.com/fillano/evolve/blob/ec5917f002a8ba3b736224d9c58a646d931defe4/lib/evolve.js
主要的改變是利用responseWrapper函數來包裝response物件並且提供一致的介面,把一些操作改寫入buffer,到post dispatch時才實際寫入response。
跑一下整合測試,發現testCookie.js沒有寫好,稍微改一下:
var Evolve = require('../lib-cov/evolve');
var testCase = require('nodeunit').testCase;
var tools = require('../lib-cov/tools');
module.exports = testCase({
"setUp": function(cb) {
this.http = require('http');
this.evolve = new Evolve({dirindex: ['index.html', 'index.htm', 'default.htm']});
this.evolve.handle('pre', tools.cookieHandler);
this.evolve.host('localhost:8443')
.get('/testcookie', function(request, response, cb) {
if(request.cookie) {
var str = ''+request.headers.cookie;
cb(false, '/testcookie', {type:'text/plain', data: str})
}else{
var str = "no cookie";
cb(false, '/testcookie', {type:'text/plain', data: str})
}
});
this.evolve.listen(8443, 'localhost');
cb();
},
"tearDown": function(cb) {
this.evolve.close();
cb();
},
"test if cookie received and appended to request": function(test) {
test.expect(2);
var cv = new Date().getTime();
var req = this.http.request({
"host": "localhost",
"port": 8443,
"path": "/testcookie",
"method": "GET",
"headers": {
"Cookie": "SID="+cv
}
}, function(response) {
var result = [];
response.on('data', function(data) {
result.push(data);
});
response.on('end', function() {
var total = 0;
var entity = '';
for(var i=0; i<result.length; i++) {
total += result[i].length;
entity += result[i].toString('ascii');
}
test.ok(entity.indexOf('SID')>-1);
test.ok(entity.indexOf(cv+"")>-1);
test.done();
});
});
req.end();
}
});
ok,這樣就可以跑完測試了。
後續
接下來要寫一個簡單的template引擎,並且考慮與model整合的情形,來做比較詳細的設計與實作。
今天的範例檔fillano-evolve-v0.0.17-0-g1b2dcab.zip,目前版本是v0.0.17。